package drds.plus.executor.cursor.cursor_metadata;

import drds.plus.executor.ExecutorException;
import drds.plus.executor.utils.Utils;
import drds.plus.sql_process.abstract_syntax_tree.configuration.ColumnMetaData;
import drds.plus.util.GeneralUtil;

import java.util.*;

public class CursorMetaDataImpl implements CursorMetaData {
    private Map<String/* 列名字 */, TableNameAndColumnIndexAndNextHolder> columnNameToTableNameAndColumnIndexAndNextHolderMap = new TreeMap<String, TableNameAndColumnIndexAndNextHolder>(String.CASE_INSENSITIVE_ORDER);
    private String tableName;
    private List<ColumnMetaData> columnMetaDataList;
    private Map<Integer, ColumnMetaData> indexToColumnMetaDataMap = new HashMap<Integer, ColumnMetaData>();
    private Integer indexRange;
    private boolean isSureLogicalIndexEqualActualIndex;


    private CursorMetaDataImpl(String tableName, List<Column> columnList, Integer indexRange) {
        super();
        this.tableName = tableName;
        this.columnMetaDataList = new ArrayList<ColumnMetaData>();
        this.indexRange = indexRange;
        int index = 0;
        for (Column column : columnList) {

            addTableNameAndColumnIndexAndNextHolder(tableName, column.getColumnName(), column.getAlias(), index);
            //
            ColumnMetaData columnMetaData = new ColumnMetaData(tableName, column.getColumnName(), column.getAlias(), column.getType(), column.getNullable());
            this.columnMetaDataList.add(columnMetaData);
            this.indexToColumnMetaDataMap.put(index, columnMetaData);
            index++;
        }
    }

    private CursorMetaDataImpl(String tableName, List<Column> columnList, List<Integer> indexList, Integer indexRange) {
        this.columnMetaDataList = new ArrayList<ColumnMetaData>();
        Iterator<Integer> indexIterator = indexList.iterator();
        this.columnMetaDataList = new ArrayList(columnList.size());

        for (Column column : columnList) {

            Integer index = indexIterator.next();
            addTableNameAndColumnIndexAndNextHolder(tableName, column.getColumnName(), column.getAlias(), index);
            //
            ColumnMetaData columnMetaData = new ColumnMetaData(tableName, column.getColumnName(), column.getAlias(), column.getType(), column.getNullable());
            this.columnMetaDataList.add(columnMetaData);
            if (!indexIterator.hasNext()) {
                throw new IllegalArgumentException("iterator and columnNameList not ruleCalculate");
            }
            this.indexToColumnMetaDataMap.put(index, columnMetaData);
        }
        this.indexRange = indexRange;

    }

    private CursorMetaDataImpl(List<ColumnMetaData> columnMetaDataList, List<Integer> indexList, Integer indexRange) {

        this.columnMetaDataList = new ArrayList(columnMetaDataList.size());
        Iterator<Integer> indexIterator = indexList.iterator();
        for (ColumnMetaData columnMetaData : columnMetaDataList) {
            String tableName = columnMetaData.getTableName();
            if (!indexIterator.hasNext()) {
                throw new IllegalArgumentException("iterator and columnNameList not ruleCalculate");
            }
            Integer index = indexIterator.next();
            addTableNameAndColumnIndexAndNextHolder(tableName, columnMetaData.getColumnName(), columnMetaData.getAlias(), index);
            //
            ColumnMetaData $columnMetaData = new ColumnMetaData(tableName, columnMetaData.getColumnName(), columnMetaData.getAlias(), columnMetaData.getType(), columnMetaData.isNullable());
            this.columnMetaDataList.add($columnMetaData);
            this.indexToColumnMetaDataMap.put(index, columnMetaData);
        }
        this.indexRange = indexRange;


    }

    private CursorMetaDataImpl(List<ColumnMetaData> columnMetaDataList, Integer indexRange) {
        super();
        this.columnMetaDataList = new ArrayList<ColumnMetaData>(columnMetaDataList);
        int index = 0;
        for (ColumnMetaData columnMetaData : columnMetaDataList) {
            String tableName = columnMetaData.getTableName();
            addTableNameAndColumnIndexAndNextHolder(tableName, columnMetaData.getColumnName(), columnMetaData.getAlias(), index);
            this.indexToColumnMetaDataMap.put(index, columnMetaData);
            index++;
        }
        this.indexRange = indexRange;
    }

    private CursorMetaDataImpl(List<ColumnMetaData> columnMetaDataList) {
        super();
        this.columnMetaDataList = new ArrayList<ColumnMetaData>(columnMetaDataList);
        int index = 0;
        for (ColumnMetaData columnMetaData : columnMetaDataList) {
            String tableName = columnMetaData.getTableName();
            addTableNameAndColumnIndexAndNextHolder(tableName, columnMetaData.getColumnName(), columnMetaData.getAlias(), index);
            //
            this.indexToColumnMetaDataMap.put(index, columnMetaData);
            index++;
        }
        this.indexRange = index;
    }

    public ColumnMetaData getColumnMetaData(Integer index) {
        if (this.indexToColumnMetaDataMap.containsKey(index)) {
            return this.indexToColumnMetaDataMap.get(index);
        }
        throw new ExecutorException("indexMapping is not in cursor cursorMetaData, indexToColumnMetaDataMap is " + this.indexToColumnMetaDataMap + ", " + this);

    }


    public Integer getIndexRange() {
        return indexRange;
    }

    public List<ColumnMetaData> getColumnMetaDataList() {
        return Collections.unmodifiableList(columnMetaDataList);
    }

    public Integer getIndex(String tableName, String columnName, String columnAlias) {
        Integer index = null;
        if (columnAlias != null) {
            index = getIndex(tableName, columnAlias);
        }
        if (index == null) {
            index = getIndex(tableName, columnName);
        }
        return index;
    }

    private Integer getIndex(String tableName, String columnName) {
        tableName = Utils.getTableName(tableName);
        TableNameAndColumnIndexAndNextHolder tableNameAndColumnIndexAndNextHolder = columnNameToTableNameAndColumnIndexAndNextHolderMap.get(columnName);
        if (tableNameAndColumnIndexAndNextHolder == null) {
            return null;
        }
        // 第一个P
        Integer index = null;
        if (tableName == null) {// hook tableName ==null.取第一个,如果没有下一个，那么无论表名是什么都返回当前的。。因为没的选择。
            return tableNameAndColumnIndexAndNextHolder.columnIndex;
        }
        index = getColumnIndex(tableNameAndColumnIndexAndNextHolder, tableName);//firstInNewCursor
        if (index != null) {
            return index;
        }
        TableNameAndColumnIndexAndNextHolder next = tableNameAndColumnIndexAndNextHolder;
        while ((next = next.next) != null) {
            index = getColumnIndex(next, tableName);
            if (index != null) {
                return index;
            }
        }
        return tableNameAndColumnIndexAndNextHolder.columnIndex;
    }

    private static Integer getColumnIndex(TableNameAndColumnIndexAndNextHolder tableNameAndColumnIndexAndNextHolder, String tableName) {
        if (tableName.equals(tableNameAndColumnIndexAndNextHolder.tableName)) {
            return tableNameAndColumnIndexAndNextHolder.columnIndex;
        }
        return null;
    }


    protected void addTableNameAndColumnIndexAndNextHolder(String tableName, String columnName, String columnAlias, Integer index) {
        tableName = Utils.getTableName(tableName);
        TableNameAndColumnIndexAndNextHolder tableNameAndColumnIndexAndNextHolder = columnNameToTableNameAndColumnIndexAndNextHolderMap.get(columnName);
        if (tableNameAndColumnIndexAndNextHolder == null) {
            tableNameAndColumnIndexAndNextHolder = new TableNameAndColumnIndexAndNextHolder(tableName, index, null);
            columnNameToTableNameAndColumnIndexAndNextHolderMap.put(columnName, tableNameAndColumnIndexAndNextHolder);
            return;
        }
        //
        boolean success = findTableNameReplaceColumnIndexAndNewTableNameAndColumnIndexAndNextHolder(tableNameAndColumnIndexAndNextHolder, tableName, index);
        if (success) {
            return;
        }
        TableNameAndColumnIndexAndNextHolder next;
        // 其它的ColumnHolder
        while ((next = tableNameAndColumnIndexAndNextHolder.next) != null) {
            success = findTableNameReplaceColumnIndexAndNewTableNameAndColumnIndexAndNextHolder(next, tableName, index);
            if (success) {
                return;
            }
            tableNameAndColumnIndexAndNextHolder = tableNameAndColumnIndexAndNextHolder.next;
        }
    }

    /**
     * 如果能找到同表名的，就替换对应的index 如果不能找到，但链表下一个为空，则构建新的ColumnHolder 放到队尾，也算成功 其他算失败
     */
    private static boolean findTableNameReplaceColumnIndexAndNewTableNameAndColumnIndexAndNextHolder(TableNameAndColumnIndexAndNextHolder tableNameAndColumnIndexAndNextHolder, String tableName, Integer columnIndex) {
        boolean success = false;
        if (tableName.equals(tableNameAndColumnIndexAndNextHolder.tableName)) {
            tableNameAndColumnIndexAndNextHolder.columnIndex = columnIndex;
            success = true;
        } else if (tableNameAndColumnIndexAndNextHolder.next == null) {
            tableNameAndColumnIndexAndNextHolder.next = new TableNameAndColumnIndexAndNextHolder(tableName, columnIndex, null);
            success = true;
        }
        return success;
    }

    public boolean isSureLogicalIndexEqualActualIndex() {
        return this.isSureLogicalIndexEqualActualIndex;
    }

    public void setIsSureLogicalIndexEqualActualIndex(boolean isSureLogicalIndexEqualActualIndex) {
        this.isSureLogicalIndexEqualActualIndex = isSureLogicalIndexEqualActualIndex;
    }

    public Iterator<ColumnNameAndTableNameAndColumnIndexAndNextHolder> columnNameAndTableNameAndColumnIndexAndNextHolderIterator() {
        return new ColumnNameAndTableNameAndColumnIndexNextHolderIterator(columnNameToTableNameAndColumnIndexAndNextHolderMap);
    }

    //
    public String toString() {
        return toStringWithInden(0);
    }

    public String toStringWithInden(int inden) {
        StringBuilder sb = new StringBuilder();
        String tabTittle = GeneralUtil.getTab(inden);
        sb.append(tabTittle).append("[");
        sb.append("cursor cursorMetaData columnName : ").append(tableName).append("\t");
        List<ColumnMetaData> columnMetaDataList = this.columnMetaDataList;
        if (columnMetaDataList != null) {
            for (ColumnMetaData columnMetaData : columnMetaDataList) {
                sb.append(columnMetaData.toStringWithInden(0)).append(":");
                sb.append(getIndex(columnMetaData.getTableName(), columnMetaData.getColumnName()));
                sb.append(" ");
            }
        }
        return sb.toString();

    }

    public static CursorMetaDataImpl buildCursorMetaData(String tableName, List<Column> columnList, List<Integer> indexList, Integer indexRange) {
        return new CursorMetaDataImpl(tableName, columnList, indexList, indexRange);
    }

    public static CursorMetaDataImpl buildCursorMetaData(String tableName, List<Column> columnList, Integer indexRange) {
        return new CursorMetaDataImpl(tableName, columnList, indexRange);
    }

    //
    public static CursorMetaDataImpl buildCursorMetaData(List<ColumnMetaData> columnMetaDataList) {
        return new CursorMetaDataImpl(columnMetaDataList);
    }

    public static CursorMetaDataImpl buildCursorMetaData(List<ColumnMetaData> columnMetaDataList, Integer indexRange) {
        return new CursorMetaDataImpl(columnMetaDataList, indexRange);
    }

    public static CursorMetaDataImpl buildCursorMetaData(List<ColumnMetaData> columnMetaDataList, List<Integer> indexList, Integer indexRange) {
        return new CursorMetaDataImpl(columnMetaDataList, indexList, indexRange);
    }


}
